#include <dalelib/maths/v3.h>

#include <math.h>
#include <dalelib/maths/mathsstuff.h>

namespace dalelib
{

#define V3_EPSILON 0.0001f

/*** constructors ***/
v3::v3() { f[0] = 0.f; f[1] = 0.f; f[2] = 0.f; }
v3::v3(const float _Value) { f[0] = _Value; f[1] = _Value; f[2] = _Value; }
v3::v3(const float _Value0, const float _Value1, const float _Value2) { f[0] = _Value0; f[1] = _Value1; f[2] = _Value2; }
const v3 v3::zero(0,0,0);
const v3 v3::one(1,1,1);
const v3 v3::randCube() { return v3(RRAND,RRAND,RRAND); }
const v3 v3::randSphere() { v3 pos(RRAND,RRAND,RRAND); while(pos.len2()>1.f) pos = v3(RRAND,RRAND,RRAND); return pos; }

/*** accessors ***/
const v3 v3::operator =(const v3& _Value) { f[0] = _Value[0]; f[1] = _Value[1]; f[2] = _Value[2]; return *this; }

/*** unary operation ***/
v3 v3::operator-() const { return v3(-f[0],-f[1],-f[2]); }

/*** scalar operations ***/
void v3::operator+=(const float _Value) { f[0] += _Value; f[1] += _Value; f[2] += _Value; }
void v3::operator-=(const float _Value) { f[0] -= _Value; f[1] -= _Value; f[2] -= _Value; }
void v3::operator*=(const float _Value) { f[0] *= _Value; f[1] *= _Value; f[2] *= _Value; }
void v3::operator/=(const float _Value) { float ff = 1.f/_Value; f[0] *= ff; f[1] *= ff; f[2] *= ff; }

v3 v3::operator+(const float _Value) const { return v3( f[0]+_Value, f[1]+_Value, f[2]+_Value ); }
v3 v3::operator-(const float _Value) const { return v3( f[0]-_Value, f[1]-_Value, f[2]-_Value ); }
v3 v3::operator*(const float _Value) const { return v3( f[0]*_Value, f[1]*_Value, f[2]*_Value ); }
v3 v3::operator/(const float _Value) const { float ff = 1.f/_Value;  return v3( f[0]*ff, f[1]*ff, f[2]*ff ); }

/*** vector operations ***/
void v3::operator+=(const v3& _Value) { f[0] += _Value[0]; f[1] += _Value[1]; f[2] += _Value[2]; }
void v3::operator-=(const v3& _Value) { f[0] -= _Value[0]; f[1] -= _Value[1]; f[2] -= _Value[2]; }
void v3::operator*=(const v3& _Value) { f[0] *= _Value[0]; f[1] *= _Value[1]; f[2] *= _Value[2]; }
void v3::operator/=(const v3& _Value) { f[0] /= _Value[0]; f[1] /= _Value[1]; f[2] /= _Value[2]; }

v3 v3::operator+(const v3& _Value) const { return v3( f[0]+_Value[0], f[1]+_Value[1], f[2]+_Value[2] ); }
v3 v3::operator-(const v3& _Value) const { return v3( f[0]-_Value[0], f[1]-_Value[1], f[2]-_Value[2] ); }
v3 v3::operator*(const v3& _Value) const { return v3( f[0]*_Value[0], f[1]*_Value[1], f[2]*_Value[2] ); }
v3 v3::operator/(const v3& _Value) const { return v3( f[0]/_Value[0], f[1]/_Value[1], f[2]/_Value[2] ); }

float v3::len2() const { return f[0]*f[0] + f[1]*f[1] + f[2]*f[2]; }
float v3::len() const { float l2=len2(); return (l2>V3_EPSILON)?sqrtf(l2):0.f;  }

v3 v3::unit() const { float l=len(); return (l>V3_EPSILON)?(*this/l):v3(0.f); }
float v3::normalise() { float l=len(); if(l>V3_EPSILON) *this/=l; else *this=v3(0.f); return l; }
v3 v3::canonicalise()
{
#define UNITY (1.0 - 1.0e-6)
	if     ( f[0] >=  UNITY ) return v3( 1,0,0);
	else if( f[0] <= -UNITY ) return v3(-1,0,0);
	else if( f[1] >=  UNITY ) return v3(0, 1,0);
	else if( f[1] <= -UNITY ) return v3(0,-1,0);
	else if( f[2] >=  UNITY ) return v3(0,0, 1);
	else if( f[2] <= -UNITY ) return v3(0,0,-1);
	else return v3::zero;
#undef UNITY
}
v3 v3::recip() const { return v3(1.f/f[0],1.f/f[1],1.f/f[2]); }
void v3::clamp(const v3& _Min,const v3& _Max)
{
	if(f[0]<_Min[0]) f[0]=_Min[0];
	if(f[0]>_Max[0]) f[0]=_Max[0];
	if(f[1]<_Min[1]) f[1]=_Min[1];
	if(f[1]>_Max[1]) f[1]=_Max[1];
	if(f[2]<_Min[2]) f[2]=_Min[2];
	if(f[2]>_Max[2]) f[2]=_Max[2];
}

//#define SATURATE(x) ((x<0.f)?0.f:(x>1.f)?1.f:x)
//v3 v3::saturate() const { return v3( SATURATE(f[0]), SATURATE(f[1]), SATURATE(f[2]) ); }
void v3::print(string _String) const { printf( "%s %.3f %.3f %.3f\n", _String.c_str(), f[0], f[1], f[2] ); }
void v3::print(string _String)       { printf( "%s %.3f %.3f %.3f\n", _String.c_str(), f[0], f[1], f[2] ); }

/*** swizzling ***/
v2 v3::xy() const { return v2( f[0], f[1] ); }
v2 v3::xz() const { return v2( f[0], f[2] ); }
v2 v3::yx() const { return v2( f[1], f[0] ); }
v2 v3::zx() const { return v2( f[2], f[0] ); }
v2 v3::yz() const { return v2( f[1], f[2] ); }
v2 v3::zy() const { return v2( f[2], f[1] ); }

v3 v3::xxx() const { return v3( f[0], f[0], f[0] ); }
v3 v3::yyy() const { return v3( f[0], f[0], f[0] ); }
v3 v3::zzz() const { return v3( f[0], f[0], f[0] ); }

v4 v3::xyz0() const { return v4( f[0], f[1], f[2], 0.f ); }
v4 v3::xyz1() const { return v4( f[0], f[1], f[2], 1.f ); }

#undef V3_EPSILON

} // namespace dalelib

